﻿using Microsoft.Xna.Framework;
using FarseerPhysics.Dynamics;
using lumo.display.object2D;
using System;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework.Graphics;
using lumo;
using System.Collections.Generic;
using FarseerPhysics.Dynamics.Contacts;

namespace game.character
{
    /// <summary>
    /// The player.
    /// </summary>
    public abstract class GameEvent : GameCharacter
    {
        /// <summary>
        /// An AI Action
        /// </summary>
        public class AIAction
        {
            public Func<bool>   ConditionFunc;
            public Func<string> TrueAction;
            public Func<string> FalseAction;
            public Func<List<string>, bool> GroupCondition;

            private bool ListIncludeAny(List<string> list, string[] Groups)
            {
                foreach (string s in Groups)
                    if (list.Contains(s))
                        return true;
                return false;
            }

            private bool ListIncludeAny(List<string> list, List<string> Groups)
            {
                foreach (string s in Groups)
                    if (list.Contains(s))
                        return true;
                return false;
            }

            private bool ListIncludeAll(List<string> list, string[] Groups)
            {
                foreach (string s in Groups)
                    if (!list.Contains(s))
                        return false;
                return true;
            }

            private bool ListIncludeAll(List<string> list, List<string> Groups)
            {
                foreach (string s in Groups)
                    if (!list.Contains(s))
                        return false;
                return true;
            }

            public void OnNotGroups(params string[] Groups) { GroupCondition = (list) => !ListIncludeAny(list, Groups); }
            public void OnGroups(params string[] Groups) { GroupCondition = (list) => ListIncludeAll(list, Groups); }
            public void OnNotGroups(List<string> Groups) { GroupCondition = (list) => !ListIncludeAny(list, Groups); }
            public void OnGroups(List<string> Groups) { GroupCondition = (list) => ListIncludeAll(list, Groups); }
            public void OnGroupsSelective(string[] Groups, string[] NotGroups) { GroupCondition = (list) => (ListIncludeAll(list, Groups) && !ListIncludeAny(list, NotGroups)); }
        };

        // Variables
        protected List<AIAction> _AIActionList;
        public List<AIAction> AIActionList
        {
            get { return _AIActionList; }
        }


        /// The tiled object.
        public bool Deleted = false;
        protected Tiled.Object _TiledObject;

        /// <summary>
        /// Constructor for RedEnemy.
        /// </summary>
        public GameEvent(LumoComponent component, GameMap map, Tiled.Object obj)
            : base(component, map)
        {
            // Setup the AI Action List
            this._AIActionList = new List<AIAction>();
            //
            _TiledObject = obj;
        }

        /// <summary>
        /// Setup the Event.
        /// </summary>
        public override void Setup()
        {
            // Setup the AI Action List
            base.Setup();
            // Setup the parameters of the event.
            SetupParameters();
            // Setup the AI for this event.
            SetupAI();
        }

        /// <summary>
        /// Setup when condition.
        /// </summary>
        public AIAction AIOnCondition(Func<bool> ConditionFunc, Func<string> Action, Func<string> ElseAction = null)
        {
            // Setup the AI Action List
            AIAction TempAction = new AIAction();
            TempAction.ConditionFunc = ConditionFunc;
            TempAction.TrueAction  = Action;
            TempAction.FalseAction = ElseAction;
            this._AIActionList.Add(TempAction);
            return TempAction;
        }

        /// <summary>
        /// Called when other caracter is in range.
        /// </summary>
        public AIAction AIOnRange(float Distance, GameCharacter Other, Func<string> Action, Func<string> ElseAction = null)
        {
            // Setup the AI Action List
            AIAction TempAction = new AIAction();
            TempAction.ConditionFunc = () => { return ((ScreenPosition - Other.ScreenPosition).Length() <= Distance); };
            TempAction.TrueAction  = Action;
            TempAction.FalseAction = ElseAction;
            this._AIActionList.Add(TempAction);
            return TempAction;
        }

        /// <summary>
        /// Called when player is OnContact.
        /// </summary>
        public AIAction AIOnContact(GameCharacter Other, Func<string> Action, Func<string> ElseAction = null)
        {
            // Setup the AI Action List
            AIAction TempAction = new AIAction();
            TempAction.ConditionFunc = () =>
            {
                ContactEdge contact = this.Body.ContactList;
                while (contact != null)
                {
                    if (contact.Other == Other.Body)
                    {
                        if (contact.Contact.IsTouching())
                            return true;
                    }
                    contact = contact.Next;
                }
                return false;
            };
            TempAction.TrueAction  = Action;
            TempAction.FalseAction = ElseAction;
            this._AIActionList.Add(TempAction);
            return TempAction;
        }

        /// <summary>
        /// Called When Update
        /// </summary>
        public AIAction AIOnUpdate(Func<string> Action)
        {
            // Setup the AI Action List
            AIAction TempAction = new AIAction();
            TempAction.ConditionFunc = () => true;
            TempAction.TrueAction = Action;
            TempAction.FalseAction = null;
            this._AIActionList.Add(TempAction);
            return TempAction;
        }

        /// <summary>
        /// Called On Timer.
        /// </summary>
        public AIAction AIOnTimer(float Initial, float Miliseconds, Func<string> Action)
        {
            // Setup the AI Action List
            AIAction TempAction = new AIAction();
            float[] Data = new float[] { 0, Miliseconds };
            TempAction.ConditionFunc = () =>
            {
                Data[0] -= (float)_LumoComponent.GameTime.ElapsedGameTime.TotalMilliseconds;
                if (Data[0] <= 0)
                {
                    Data[0] = Data[1];
                    return true;
                }
                return false;
            };
            TempAction.TrueAction = Action;
            TempAction.FalseAction = null;
            this._AIActionList.Add(TempAction);
            return TempAction;
        }
        public AIAction AIOnTimer(float Miliseconds, Func<string> Action)
        {
            return AIOnTimer(0, Miliseconds, Action);
        }

        /// <summary>
        /// Abstract method to create the body.
        /// </summary>
        protected abstract override void CreateBody();

        /// <summary>
        /// Abstract method to create the body.
        /// </summary>
        protected abstract void SetupParameters();

        /// <summary>
        /// Abstract method to create the body.
        /// </summary>
        protected abstract void SetupAI();

        /// <summary>
        /// Update the character.
        /// </summary>
        /// <param name="component">Update using lumo</param>
        public override void Update( LumoComponent component )
        {
            base.Update(component);

            List< string > ActiveGroups = new List< string >();

            foreach (AIAction Item in _AIActionList)
            {
                Func<string> Act = null;
                if ( Item.ConditionFunc() )
                {
                    Act = Item.TrueAction;
                }
                else
                    Act = Item.FalseAction;
                if (Act != null)
                {
                    if ((Item.GroupCondition != null) && (!Item.GroupCondition(ActiveGroups)))
                        continue;
                    string Group = Act();
                    if (Act != null)
                        ActiveGroups.Add(Group);
                }
            }
        }

    }

}